#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "listaligada.c"
#include "mpi.h"

#define BLOCOS_POR_NO 10  

int main(int argc, char **argv){

	int rank, size;
	char *message;

	MPI_Status status;
    Apontador pos=NULL;
    char c, palavra[50];
	int i=0, j=0, k=0, letra, tamanhoPalavra=3, totalBlocos, flagRank=0, auxtam, qtTotal=0, aux=0, result, resultAux=0;
	long fileSize, bytesPorBloco;
	float  faltando=0.9;
	time_t start,end;
	FILE *file;


	MPI_Init(&argc, &argv);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
		
	if (rank==0){
		file=fopen("teste2.txt", "r+");
		if (file==NULL){
			printf("Erro ao abrir o arquivo.\n");
			exit(1);
		}
		
		//Calcula quantidade de bytes no arquivo
		fseek (file, 0, SEEK_END);
		fileSize= ftell(file);
		rewind (file);
		//Calcula quantidade de palavras no arquivo		
		while(fscanf(file,"%c", &c)>0){
			if(c !=' ' && c !=',' && c !='.' && c !='!' && c !='?' && c!='\n' && c!='-')
				i=1;
			else if(i!=0){			
				//Zera o contador para começar a contar a proxima palavra
				i=0;
				qtTotal++;
			}
		}
		//printf("qtTotal: %d\n", qtTotal);	
		rewind (file);

		totalBlocos = (size-1)*(BLOCOS_POR_NO);
		bytesPorBloco=fileSize/totalBlocos;
		printf("fileSize: %ld, bytesPorBloco: %ld TotalBlocos: %d\n", fileSize, bytesPorBloco, totalBlocos);
		
		message=(char*)malloc(bytesPorBloco+1);
		if(message==NULL){
			exit(1);
		}
		
		//Nó que receberá a mensagem
		j=1;
		//controle da msg
		i=-1;
		//controle do file
		aux=0;

		while(aux<fileSize){
			if(i<bytesPorBloco-1){
				aux++;
				//Leitura da mensagem
				if(aux<fileSize){
					i++;
					fscanf(file,"%c", &message[i]);
					//printf("Caracter: %c aux: %d, i: %d\n", message[i], aux, i);
				}
				//Para ultimo bloco
				else{
					message[++i]='\0';
					MPI_Send(message,			//buffer da mensagem
							 i+1,				//quantidade de itens
							 MPI_CHAR,			//de tipo char
							 j,					//rank de quem vai receber
							 0,					//TAG
							 MPI_COMM_WORLD);	//comunicador default
					//printf("3.Sending to node=%d, tamanho=%d, message=%s\n", j, i+1, message);
					printf("3.Sending to node=%d, tamanho=%d\n", j, i+1);
				}
			}
			//Quanto chega no tamanho maximo da mensagem
			else{
				fscanf(file,"%c", &message[i+1]);
				fseek(file, -1, SEEK_CUR);
				if(message[i+1]!=' ')
					//Volta para o ultimo caracter ' ', para não cortar palavra ao meio 				
					while((message[i]>=65 && message[i]<=90) || (message[i]>=97 && message[i]<=122)){
						i--;
						aux--;
						fseek(file, -1, SEEK_CUR);
					//printf("Retornado: %c, aux: %d\n", message[i], aux);
					}
				message[++i]='\0';
				//Quando todos os slaves já receberam pelo menos um work unit
				if(flagRank!=0){
					//Recebe resultado de um slave
					MPI_Recv(&result,       //buffer da mensagem
							 1,             //quantidade de itens
							 MPI_INT,	    //de tipo int
							 MPI_ANY_SOURCE,//recebe de qualquer source
							 MPI_ANY_TAG,   //qualquer TAG de mensagem
							 MPI_COMM_WORLD,//comunicador default
							 &status);      //infos
					resultAux+=result;
					if(resultAux==(int)(qtTotal*faltando)){
						printf("Terminado: %d%% em %.2lf segundos\n", (int)(100-faltando*100), difftime (end,start));
						faltando-=0.1;
					}
    				//Manda nova unidade de trabalho
					MPI_Send(message,		//buffer da mensagem
						 i+1,				//quantidade de itens
						 MPI_CHAR,			//de tipo char
						 status.MPI_SOURCE, //manda para quem acabou de enviar Recv
						 0,					//TAG
						 MPI_COMM_WORLD);   //comunicador default
					//printf("2.Sending to node=%d, tamanho=%d, message=%s\n", status.MPI_SOURCE, i+1, message);
					printf("2.Sending to node=%d, tamanho=%d\n", status.MPI_SOURCE, i+1);
				}
				//Primeira work unit de cada slave
				else{
					MPI_Send(message,			// buffer da mensagem
							 i+1,				// quantidade de itens
							 MPI_CHAR,			// de tipo char
							 j,					// rank de quem vai receber
							 0,					// TAG
							 MPI_COMM_WORLD);	// comunicador default
					//printf("1.Sending to node=%d, tamanho=%d, message=%s\n", j, i+1, message);
					printf("1.Sending to node=%d, tamanho=%d\n", j, i+1);
					j++;
					if(j==size)
						flagRank=1;
				}//else do if(flagRank!=0)
				i=-1;
			}//else do if(i<bytesPorBloco-1)
		}//while(aux<fileSize)
		//Recebe o ultimo resultado
		MPI_Recv(&result,	//buffer da mensagem
			 1,				//quantidade de itens
			 MPI_INT,		//de tipo int
			 MPI_ANY_SOURCE,//recebe de qualquer source
			 MPI_ANY_TAG,	//qualquer TAG de mensagem
			 MPI_COMM_WORLD,//comunicador default
			 &status);		//infos
	}//if (rank==0)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	else{
		TipoLista L1, L2, L3, L4, L5;
		TipoItem I;
		int  qtPalavras=0, count=0, qtTotalPalavras;
		
		//O segundo argumento é o tamanho MAX da msg
		MPI_Recv(message,	//buffer da mensagem
			 100000,		//quantidade MAX de itens
			 MPI_CHAR,		//de tipo int
			 MPI_ANY_SOURCE,//recebe de qualquer source
			 0,				//qualquer TAG de mensagem
			 MPI_COMM_WORLD,//comunicador default
			 &status);		//infos
		MPI_Get_count(&status, MPI_CHAR, &count);
		//printf("\nCount: %d My rank= %d Message:.%s.\n", count, rank, message);
		printf("\nCount: %d My rank= %d\n", count, rank);
		
		srand(time(NULL));
		
		InicializaLista(&L1);  
		InicializaLista(&L2);
		InicializaLista(&L3);  
		InicializaLista(&L4);  
		InicializaLista(&L5);
	  	
		time (&start);	
		//Le as palavras ignorando . , ! ? 
		//Salva como minusculas

		aux=0;
		while(aux<count){
			if(message[aux] !=' ' && message[aux] !=',' && message[aux] !='.' && message[aux] !='!' && message[aux] !='?' && message[aux]!='\n' && message[aux]!='-' && message[aux]!='\0'){
				I.Chave[i++] = tolower(message[aux]);
			}
			else if(i!=0){
				I.Chave[i] = '\0';
				//printf("Palavra: %s\n", I.Chave);
				if(i<=5){
					switch(i){
						case 1:
							//printf("Rank: %d, 1.Palavra: %s\n",rank, I.Chave);
							InsereLista(&L1, I, &qtPalavras);
						break;
						case 2:
							//printf("Rank: %d, 2.Palavra: %s\n",rank, I.Chave);
							InsereLista(&L2, I,&qtPalavras );
							break;
						case 3:
							//printf("Rank: %d, 3.Palavra: %s\n",rank, I.Chave);		
							InsereLista(&L3, I, &qtPalavras);
							break;
						case 4:
							//printf("Rank: %d, 4.Palavra: %s\n",rank, I.Chave);
							InsereLista(&L4, I, &qtPalavras);
							break;
						case 5:
							//printf("Rank: %d, 5.Palavra: %s\n",rank, I.Chave);
							InsereLista(&L5, I, &qtPalavras);
							break;
						default:
							printf("Tamanho da palavra inválido. O programa será abortado.\n");
					}
				}
				else{
					for(j=0;j<i;j++)
						palavra[j] = I.Chave[j];
					palavra[j]='\0';
					j=0;
					//Separa a palavra em grupos de 3, e o restante, caso necessario
					while(j<i){
						for(k=0;k<tamanhoPalavra;k++)
							I.Chave[k]=palavra[j++];
						I.Chave[tamanhoPalavra]='\0';
						switch(tamanhoPalavra){
							case 1:
								//printf("Rank: %d, 1.Palavra: %s\n",rank, I.Chave);
								InsereLista(&L1, I, &qtPalavras);
							break;
							case 2:
								//printf("Rank: %d, 2.Palavra: %s\n",rank, I.Chave);
								InsereLista(&L2, I,&qtPalavras);
								break;
							case 3:
								//printf("Rank: %d, 3.Palavra: %s\n",rank, I.Chave);
								InsereLista(&L3, I, &qtPalavras);
								break;
							case 4:
								//printf("Rank: %d, 4.Palavra: %s\n",rank, I.Chave);
								InsereLista(&L4, I, &qtPalavras);
								break;
							case 5:
								//printf("Rank: %d, 5.Palavra: %s\n",rank, I.Chave);
								InsereLista(&L5, I, &qtPalavras);
								break;
							default:
								printf("Tamanho da palavra inválido. O programa será abortado.\n");
						}
						auxtam=i-j;
						if(auxtam==4)
							tamanhoPalavra=4;
						if(auxtam<3)
							tamanhoPalavra=auxtam;
					}
				}
				//Zera o contador para começar a ler a proxima palavra
				i=0;
				//Redefine o tamanho default de 3
				tamanhoPalavra=3;
			}
			aux++;
		}
		qtTotalPalavras=qtPalavras;
		//Gera palavras e tenta consumir, enquanto tiverem palavras não geradas
		while(qtPalavras>0){
			
			//Determina o tamanho da palavra a ser gerada. Varia de 1 a 5
			tamanhoPalavra = rand()%5+1;
			
			//Gera a palavra do tamanho determinado
			for(i=0;i<tamanhoPalavra;i++){	
				letra = rand()%26+97;
				palavra[i]=letra;
			}
			palavra[i]='\0';
			//Procura a palavra na lista apropriada
			switch(tamanhoPalavra){
				case 1:
					pos = PesquisaLista(&L1, palavra);
					if(pos!=NULL){
						//printf("My rank: %d, 1.Palavra encontrada: %s\n", rank, palavra);
						RemoveLista(&L1, pos);
						qtPalavras--;
					}
					break;
				case 2:
					pos = PesquisaLista(&L2, palavra);
					if(pos!=NULL){
						//printf("My rank: %d, 2.Palavra encontrada: %s\n", rank, palavra);
						RemoveLista(&L2, pos);
						qtPalavras--;
					}
					break;
				case 3:
					pos = PesquisaLista(&L3, palavra);
					if(pos!=NULL){
						//printf("My rank: %d, 3.Palavra encontrada: %s\n", rank, palavra);
						RemoveLista(&L3, pos);
						qtPalavras--;
					}
					break;
				case 4:
					pos = PesquisaLista(&L4, palavra);
					if(pos!=NULL){
						//printf("My rank: %d, 4.Palavra encontrada: %s\n", rank, palavra);
						RemoveLista(&L4, pos);
						qtPalavras--;
					}
					break;
				case 5:
					pos = PesquisaLista(&L5, palavra);
					if(pos!=NULL){
						//printf("My rank: %d, 5.Palavra encontrada: %s\n", rank, palavra);
						RemoveLista(&L5, pos);
						qtPalavras--;
					}
					break;
				default:
					printf("Tamanho da palavra inválido. O programa será abortado.\n");
					exit(1);
			}
		}
		MPI_Send(&qtTotalPalavras, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
		printf("Rank: %d sending to master, message=%d\n", rank, qtTotalPalavras);
			
		printf("\nFIM\n");
	}
	MPI_Finalize();
return 0;
}